﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using devtm.Aop.Behavior;
using devtm.Aop.Event;
using devtm.Aop.Extensions;
using Mono.Cecil;

namespace devtm.Aop.Convert
{


    //[System.Diagnostics.DebuggerStepThrough]
    public class MethodValidator
    {

        private Dictionary<string, object> _dic = new Dictionary<string, object>();
        private Hashtable _dicTraitement = new Hashtable();


        public virtual bool Validate(MethodDefinition method)
        {
            return _dic.ContainsKey(method.FullName);
        }

        public virtual void MethodUpdated(MethodDefinition method)
        {            
            _dic.Add(method.FullName, method);

        }


        private Container GetContainer(object key)
        {

            Container retour = null;

            if (_dicTraitement.ContainsKey(key))
                retour = _dicTraitement[key] as Container;
            else
            {
                retour = new Container(key);
                _dicTraitement.Add(key, retour);
            }

            return retour;

        }


        private void DelContainer(object key)
        {

            if (_dicTraitement.ContainsKey(key))
                 _dicTraitement.Remove(key);

        }


        internal void Check(ICustomAttributeProvider attributeProvider)
        {

            Container retour = GetContainer(attributeProvider);

            foreach (CustomAttribute item in attributeProvider.CustomAttributes)
            {

                TypeDefinition t = IsAopAttribute(item.AttributeType.Resolve());

                if (t != null)
                {
                    ContainerTypeDefiniton r2 = retour.Get(t);
                    r2.Add(item);
                }

            }

        }


        private static TypeDefinition IsAopAttribute(TypeDefinition item)
        {


            foreach (TypeReference t in item.Interfaces)
            {

                if (t.Name == "IIntercept")
                    return item;

                var p = IsAopAttribute(t.Resolve());
                if (p != null)
                    return p;

            }


            if (item.BaseType != null)
            {
                var p = IsAopAttribute(item.BaseType.Resolve());
                if (p != null)
                    return p;
            }

            

            return null;
        }


        internal void UnCheck(ICustomAttributeProvider method)
        {
            DelContainer(method);
        }


        internal IEnumerable<Mono.Cecil.CustomAttribute> GetBeheviors(ICustomAttributeProvider provider)
        {

            Hashtable tb = new Hashtable();
            foreach (Container item in _dicTraitement.Values)
                foreach (ContainerTypeDefiniton i2 in item.Get())
                    foreach (Mono.Cecil.CustomAttribute i3 in i2.Get())
                    {

                        string t = i3.AttributeType.FullName;

                        bool ignore = IsIgnore(i3);

                        if (ignore)
                        {
                            if (tb.ContainsKey(t))
                                tb.Remove(t);
                        }
                        else
                        {
                            if (!tb.ContainsKey(t))
                                tb.Add(t, i3);
                        }
                        

                    }

            foreach (Mono.Cecil.CustomAttribute i3 in tb.Values)
                yield return i3;

        }

        private static bool IsIgnore(Mono.Cecil.CustomAttribute i3)
        {
            
            foreach (CustomAttributeNamedArgument p in i3.Properties)
                if (p.Name == "Behevior")
                {

                    var t = (devtm.Aop.Behavior.BehaviorEnum)((int)p.Argument.Value);
                    if (t == BehaviorEnum.Ignore)
                    return true;
                }
            return false;
            
        }


        internal InterceptorContainer ConvertBehevior(List<CustomAttribute> lst)
        {


            InterceptorContainer c = new InterceptorContainer();

            foreach (CustomAttribute item in lst)
            {
                var t = item.AttributeType.Resolve();
                GetMethod(t, item, c);
            }
            return c;

        }


        internal InterceptorContainer GetBoxBehevior(MethodDefinition method)
        {
            var lst = GetBeheviors(method).ToList();
            var ret = ConvertBehevior(lst);
            return ret;
        }

        private static void GetMethod(TypeDefinition type, CustomAttribute reelType, InterceptorContainer container)
        {

            foreach (MethodDefinition method in type.Methods)
                if (method.HasCustomAttributes)
                    foreach (CustomAttribute item in method.GetCustomAttribute(typeof(InterceptEventAttribute)))
                    {

                        CustomAttributeArgument p1 = item.ConstructorArguments[0];
                        InterceptEnumEventType v1 = (InterceptEnumEventType)p1.Value;

                        if (method.Parameters.Count != 3)
                            throw new NotSupportedException("Not supported method signature for intercept injection");

                        ParameterDefinition pa1 = method.Parameters[0];
                        if (pa1.ParameterType.Name != "Object")
                            throw new NotSupportedException("Not supported method signature for intercept injection");

                        ParameterDefinition pa2 = method.Parameters[1];
                        if (pa2.ParameterType.Name != "InterceptEventArgs")
                            throw new NotSupportedException("Not supported method signature for intercept injection");

                        ParameterDefinition pa3 = method.Parameters[2];
                        if (pa3.ParameterType.Name != "Int32")
                            throw new NotSupportedException("Not supported method signature for intercept injection");

                        TypeDefinition t = item.AttributeType.Resolve();

                        container.Add(v1, method, reelType.Properties, reelType.GetHashCode(), reelType.AttributeType.Resolve());

                    }


            if (type.BaseType != null)
            {
                var ba = type.BaseType.Resolve();
                GetMethod(ba, reelType, container);
            }

        }


    }
}
